package main.utils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
 * 工具包
 * @author LiuXingWu
 * @create 2021-01-27 11:53
 */
public class UTILS {
    /**
     * 从文件中读取数据，并计算每个结果选项的概率值存入对应的map项中
     * @param fileName
     */
    public static List<String[]> readInfoFromFile(String fileName) {
        List<String[]> list = new ArrayList<String[]>();    // 文件读取的结果
        // 初始化统计指标
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(new File(fileName)));
            String inputData; // 文件每行读入的内容
            while ((inputData = reader.readLine()) != null) {
                // 按行读入数据
                String[] tempString = inputData.split(",");
                list.add(tempString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return list;    // 返回结果
        }
    }

    /**
     * 朴素贝叶斯分类器数据预处理，返回预处理的结果，同时将结果写入文件：nbc_breast_cancer
     * 文件数据格式为：
     * <value1>，<value2>···<value9>， <label>
     *     字段含义：
     * 1. Clump Thickness: 1 - 10
     * 2. Uniformity of Cell Size: 1 - 10
     * 3. Uniformity of Cell Shape: 1 - 10
     * 4. Marginal Adhesion: 1 - 10
     * 5. Single Epithelial Cell Size: 1 - 10
     * 6. Bare Nuclei: 1 - 10
     * 7. Bland Chromatin: 1 - 10
     * 8. Normal Nucleoli: 1 - 10
     * 9. Mitoses: 1 - 10
     * label.Class: (2 for benign, 4 for malignant)
     * @param srcDataList 从原始数据文件读取出来的数据
     * @return 数据预处理后的结果
     */
    public static List<double[]> NBCDataProcessing(List<String[]> srcDataList) {
        List<double[]> resultDataList = new ArrayList<double[]>();
        int len = srcDataList.get(0).length;    // 每个样本数据的个数
        // 获取目标数据的格式，即将原数据第一个元素去掉。其他的元素一次前移一位
        for (String[] str : srcDataList) {
            double[] tempstring = new double[len - 1];
            for (int i = 1; i < len; i++) {
                if (str[i].equals("?")) {str[i] = "0";}    // 遇见缺失值，先置为零，最后用平均值填充
                tempstring[i - 1] = Double.parseDouble(str[i]);
            }
            resultDataList.add(tempstring);
        }
        resultDataList = missingValueProcessing(resultDataList);    // 缺失值填充

        // 文件写入操作
        File file = new File("nbc_breast_cancer");    // 指定的文件
        byte[] contentInBytes = new byte[1024];    // 写入文件缓冲区
        String content;    // 文件写入的内容
        // 将数据内容写入文件
        try (FileOutputStream fop = new FileOutputStream(file)) {
            // 文件不存在则创建
            if (!file.exists()) {
                file.createNewFile();
            }
            // 按照指定的内容格式，写入文件
            for (double[] str : resultDataList) {
                content = "";
                for (int i = 0; i < str.length; i++) {
                    content += str[i] + ", ";
                }
                content += "\r\n";
                contentInBytes = content.getBytes();
                fop.write(contentInBytes);
                fop.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            return resultDataList;

        }
    }

    /**
     * 支持向量机数据预处理，返回预处理的结果，同时将结果写入文件：svm_breast_cancer
     * 同时将每个样本的分类结果存入文件：svm_breast_cancer_result
     * 文件数据格式为：
     * <label> <index1>:<value1> <index2>:<value2>···<index10>:<value10>
     *     字段含义：
     * label.Class: (2 for benign, 4 for malignant)
     * 1. Sample code number: id number
     * 2. Clump Thickness: 1 - 10
     * 3. Uniformity of Cell Size: 1 - 10
     * 4. Uniformity of Cell Shape: 1 - 10
     * 5. Marginal Adhesion: 1 - 10
     * 6. Single Epithelial Cell Size: 1 - 10
     * 7. Bare Nuclei: 1 - 10
     * 8. Bland Chromatin: 1 - 10
     * 9. Normal Nucleoli: 1 - 10
     * 10. Mitoses: 1 - 10
     * @param srcDataList 从原始数据文件读取出来的数据
     * @return 数据预处理后的结果
     */
    public static List<double[]> SVMDataProcessing(List<String[]> srcDataList) {
        List<double[]> resultDataList = new ArrayList<double[]>();
        int len = srcDataList.get(0).length;    // 每个样本数据的个数
        // 获取目标数据的格式，即将原数据最后一个元素放在第一个。其他的元素一次后移一位
        for (String[] str : srcDataList) {
            double[] tempstring = new double[len];
            tempstring[0] = Double.parseDouble(str[len - 1]);
            for (int i = 0; i < len - 1; i++) {
                if (str[i].equals("?")) {str[i] = "0";}    // 遇见缺失值，先置为零，最后用平均值填充
                tempstring[i + 1] = Double.parseDouble(str[i]);
            }
            resultDataList.add(tempstring);
        }
        resultDataList = missingValueProcessing(resultDataList);    // 缺失值填充

        // 文件写入操作
        File file = new File("svm_breast_cancer");    // 指定的文件
        byte[] contentInBytes = new byte[1024];    // 写入文件缓冲区
        String content;    // 文件写入的内容
        // 将数据内容写入文件
        try (FileOutputStream fop = new FileOutputStream(file)) {
            // 文件不存在则创建
            if (!file.exists()) {
                file.createNewFile();
            }
            // 按照指定的内容格式，写入文件
            for (double[] str : resultDataList) {
                content = "";
                content += str[0] + " ";
                for (int i = 1; i < str.length; i++) {
                    content += i + ":" + str[i] + " ";
                }
                content += "\r\n";
                contentInBytes = content.getBytes();
                fop.write(contentInBytes);
                fop.flush();
            }

            // 将每个样本的结果分离出来，这一步和前一步可以合并，但我比较懒，就不做啦
            File file2 = new File("svm_breast_cancer_result");
            try (FileOutputStream fop2 = new FileOutputStream(file2)){
                // 文件不存在则创建
                if (!file2.exists()) {
                    file2.createNewFile();
                }
                // 按照指定的内容格式，写入文件
                for (double[] str : resultDataList) {
                    content = "";
                    content += str[0];    // 每个样本的结果

                    content += "\r\n";
                    contentInBytes = content.getBytes();
                    fop2.write(contentInBytes);
                    fop2.flush();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            return resultDataList;

        }

    }

    /**
     * 求每个属性的平均值
     * @param srcData
     * @return
     */
    public static double[] calAvg(List<double[]> srcData) {
        int len = srcData.get(0).length;    // 数组的长度
        double[] result = new double[len];
        // 求每个属性的平均值
        for (double[] d : srcData) {
            for (int i = 0; i < len; i++) {
                result[i] += d[i];
            }
        }
        for (int i = 0; i < len; i++) {
            result[i] /= srcData.size();
        }
        return result;
    }

    /**
     * 缺失值处理，用属性平均值填充缺失值
     * @param srcData
     * @return
     */
    public static List<double[]> missingValueProcessing(List<double[]> srcData) {
        List<double[]> resultList = new ArrayList<double[]>();
        double[] avgValue = calAvg(srcData);    // 计算每组的平均值
        for (double[] d : srcData) {
            double[] temp = new double[avgValue.length];
            for (int i = 0; i < d.length; i++) {
                if (d[i] == 0) { d[i] = avgValue[i];}    // 用属性平均值填充缺失值
                temp[i] = d[i];
            }
            resultList.add(temp);
        }
        return resultList;
    }

    /**
     * 计算方差
     * @param srcData
     * @return
     */
    public static double[] calVariance(List<double[]> srcData) {
        int len = srcData.get(0).length;
        double[] variance = new double[len];
        double[] avergae = calAvg(srcData);    // 计算均值
        // 计算均值
        for (double[] d : srcData) {
            for (int i = 0; i < len; i++) {
                variance[i] += Math.pow(d[i] + avergae[i], 2);
            }
        }
        for (int i = 0; i < len; i++) {
            variance[i] /= srcData.size();
        }
        return variance;
    }
}
